//Random Game Engine
//Copyright (C) 2013  Nikolay Dionisov

//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.



#ifndef __THREADSAFE_QUEUE__H_INCLUDED__
#define __THREADSAFE_QUEUE__H_INCLUDED__

#include <list>
#include <queue>
#include <map>

#include <mutex>
#include <condition_variable>

namespace re
{
	namespace ut
	{
		template<class T>
		class RealtimeQueue
		{
		public:
			bool empty()
			{
				mutex_.lock();
				bool status = queue_.empty();
				mutex_.unlock();
				return status;
			}
			void push(T* data)
			{
				mutex_.lock();
				queue_.push(*data);
				condition_var.notify_one();
				mutex_.unlock();
			}
			void wait_pop(T* data)
			{
				std::unique_lock<std::mutex> lock(mutex_);
				while(queue_.empty())
				{
					condition_var.wait(lock);
				}
				*data = queue_.front();
				queue_.pop();
			}
			bool try_pop(T* data)
			{
				mutex_.lock();
				if(queue_.empty())
				{
					mutex_.unlock();
					return false;
				}
				*data = queue_.front();
				queue_.pop();
				mutex_.unlock();
				return true;
			}
		private:
			std::queue<T> queue_;
			std::condition_variable condition_var;
			mutable std::mutex mutex_;
		};

		template<class T>
		class RealtimeList
		{
		public:
			RealtimeList() {}
			~RealtimeList() {}

		private:
			mutable std::mutex mutex_;
			std::condition_variable condition_var;
			std::list<T> list_;

		public:
			bool empty()
			{
				mutex_.lock();
				bool ret = list_.empty();
				mutex_.unlock();
				return ret;
			}
			
			void push_back(T* data)
			{
				mutex_.lock();
				list_.push_back(*data);
				condition_var.notify_one();
				mutex_.unlock();
			}
			void push_front(T* data)
			{
				mutex_.lock();
				list_.push_front(*data);
				condition_var.notify_one();
				mutex_.unlock();
			}

			void wait_pop_front(T* data)
			{
				std::unique_lock<std::mutex> lock(mutex_);
				while(list_.empty())
				{
					condition_var.wait(lock);
				}
				*data = list_.front();
				list_.pop_front();
			}

			void wait_pop_back(T* data)
			{
				std::unique_lock<std::mutex> lock(mutex_);
				while(list_.empty())
				{
					condition_var.wait(lock);
				}
				*data = list_.back();
				list_.pop_back();
			}
			bool try_pop_back(T* data)
			{
				mutex_.lock();
				if(list_.empty())
				{
					mutex_.unlock();
					return false;
				}
				*data = list_.back();
				list_.pop_back();
				mutex_.unlock();
			}
			bool try_pop_front(T* data)
			{
				mutex_.lock();
				if(list_.empty())
				{
					mutex_.unlock();
					return false;
				}
				*data = list_.front();
				list_.pop_front();
				mutex_.unlock();
			}

		};

		template<class T, class Y>
		class RealtimeMap
		{
		public:
			RealtimeMap() {}
			~RealtimeMap() {}

			void insert(T* key, Y* value)
			{
				mutex_.lock();
				map_[*key] = *value;
				mutex_.unlock();
			}
			bool empty()
			{
				mutex_.lock();
				bool data = map_.empty();
				mutex_.unlock();
				return data;
			}
			void erase(T* key)
			{
				mutex_.lock();
				map_.erase(*key);
				mutex_.unlock();
			}
			bool find(const T *key, Y* data)
			{
				mutex_.lock();
				std::map<T,Y>::iterator i = map_.find(*key);
				if(i == map_.end())
				{
					mutex_.unlock();
					return false;
				}
				*data = i->second;
				mutex_.lock();
				return true;
			}
			std::map<T,Y>& Lock()
			{
				mutex_.lock();
				return map_;
			}
			void Unlock()
			{
				mutex_.unlock();
			}
			std::map<T,Y> map_;
			mutable std::mutex mutex_;
		};
	}
}
#endif